package org.springframework.mybatis.utils;

import org.springframework.mybatis.config.Configuration;

import java.sql.*;
import java.util.List;

/**
 * <p>JDBC操作工具类</p>
 * @author Bosen
 * @date 2021/9/27 21:16
 */
public class JDBCUtils {
    /**
     * <p>连接数据库必要的信息</p>
     */
    private static String driver, url, username, password;

    /**
     * <p>连接对象缓存</p>
     */
    private static Connection connection;
    
    /**
     * <p>初始化数据库信息</p>
     */
    public static void init(Configuration config) {
        driver = config.getDriver();
        url = config.getUrl();
        username = config.getUsername();
        password = config.getPassword();
    }

    /**
     * <p>加载数据库驱动</p>
     */
    public static boolean load() {
        try {
            Class.forName(driver);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }

    /**
     * <p>创建连接</p>
     */
    public static Connection connect() {
        // 加载数据库驱动
        load();
        try {
            if (connection == null || connection.isClosed()) {
                connection = DriverManager.getConnection(url, username, password);
                // 手动提交事务
                connection.setAutoCommit(false);
            }
        } catch (SQLException e) {
            e.printStackTrace();
            throw new RuntimeException("数据库连接失败~!!");
        }
        return null;
    }
    
    /**
     * <p>创建Statement对象</p>
     */
    public static Statement createStatement() {
        // 创建连接
        connect();
        try {
            return connection.createStatement();
        } catch (SQLException e) {
            throw new RuntimeException("创建Statement对象失败~!!");
        }
    }

    /**
     * <p>创建PreparedStatement对象</p>
     * @
     */
    private static PreparedStatement preparedStatement(String sql) {
        connect();
        try {
            return connection.prepareStatement(sql,
                    ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
        } catch (SQLException e) {
            throw new RuntimeException("创建prepareStatement对象失败~!!");
        }
    }

    /**
     * <p>提交事务</p>
     */
    private static void commit(Connection conn) {
        if (conn != null) {
            try {
                conn.commit();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * <p>事务回滚</p>
     */
    private static void rollback(Connection conn) {
        if (conn != null) {
            try {
                conn.rollback();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * <p>类型的转换</p>
     */
    private static Object typeof(Object object) {
        Object r = object;
        if (object instanceof java.sql.Timestamp) {
            return r;
        }
        // 将 java.util.Date 转成 java.sql.Date
        if (object instanceof java.util.Date) {
            java.util.Date d = (java.util.Date) object;
            r = new java.sql.Date(d.getTime());
            return r;
        }
        // 将 Character 或 char 变成 String
        if (object instanceof Character || object.getClass() == char.class) {
            r = String.valueOf(object);
            return r;
        }
        return r;
    }

    /**
     * <p>查询语句</p>
     */
    public static ResultSet query(String sql, List<Object> params) {
        if (sql == null || sql.trim().isEmpty() || !sql.trim().toLowerCase().startsWith("select")) {
            throw new RuntimeException("你的SQL语句为空或不是查询语句");
        }
        try {
            if (params.size() > 0) {
                // 有参数，使用PrepareStatement对象
                PreparedStatement preparedStatement = preparedStatement(sql);
                for (int i = 0; i < params.size(); i++) {
                    preparedStatement.setObject(i+1, params.get(i));
                }
                return preparedStatement.executeQuery();
            } else {
                // 没有参数，使用Statement对象
                Statement statement = createStatement();
                return statement.executeQuery(sql);
            }
        } catch (SQLException e) {
            e.printStackTrace();
            throw new RuntimeException("sql语句执行失败~!!");
        }
    }

    /**
     * <p>除查找、插入外的其他操作</p>
     */
    public static int execute(String sql, Object... params) {
        if (sql == null || sql.trim().isEmpty() || sql.trim().toLowerCase().startsWith("select")) {
            throw new RuntimeException("你的sql语句为空或有错~!!");
        }
        // 记录sql语句执行成功与否
        int line;

        sql = sql.trim().toLowerCase();

        // 当前Statement连接对象，用于事务回滚
        Connection conn = null;

        try {
            if (params.length > 0) {
                // 有参数，使用PreparedStatement对象
                PreparedStatement preparedStatement = preparedStatement(sql);
                // 获取当前连接对象
                conn = preparedStatement.getConnection();
                // 设置参数
                for (int i = 0; i < params.length; i++) {
                    preparedStatement.setObject(i+1, typeof(params[i]));
                }
                // 执行语句
                line = preparedStatement.executeUpdate();
                // 提交事务
                commit(conn);
            } else {
                // 没有参数，使用Statement对象
                Statement statement = createStatement();
                // 获取当前连接对象
                conn = statement.getConnection();
                // 执行语句
                line = statement.executeUpdate(sql);
                // 提交事务
                commit(conn);
            }
        } catch (SQLException e) {
            // 回滚事务
            rollback(conn);
            e.printStackTrace();
            throw new RuntimeException("sql语句执行失败~!!");
        }
        return line;
    }

    /**
     * <p>释放资源</p>
     */
    public static void release(Object cloaseable) {
        if (cloaseable != null) {
            if (cloaseable instanceof ResultSet) {
                ResultSet rs = (ResultSet) cloaseable;
                try {
                    rs.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if (cloaseable instanceof Statement) {
                Statement st = (Statement) cloaseable;
                try {
                    st.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if (cloaseable instanceof Connection) {
                Connection c = (Connection) cloaseable;
                try {
                    c.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
